This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.
Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.
library(xgboost)
载入程辑包:‘xgboost’
The following object is masked from ‘package:IRanges’:
slice
The following object is masked from ‘package:plotly’:
slice
The following object is masked from ‘package:dplyr’:
slice
library(Matrix)
载入程辑包:‘Matrix’
The following object is masked from ‘package:S4Vectors’:
expand
The following objects are masked from ‘package:tidyr’:
expand, pack, unpack
library(mclust)
__ ___________ __ _____________
/ |/ / ____/ / / / / / ___/_ __/
/ /|_/ / / / / / / / /\__ \ / /
/ / / / /___/ /___/ /_/ /___/ // /
/_/ /_/\____/_____/\____//____//_/ version 5.4.9
Type 'citation("mclust")' for citing this R package in publications.
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
─ Attaching packages ───────────────────────────────────────── tidyverse 1.3.1 ─
✓ tibble 3.1.5 ✓ stringr 1.4.0
✓ readr 2.0.2 ✓ forcats 0.5.1
✓ purrr 0.3.4
─ Conflicts ────────────────────────────────────────── tidyverse_conflicts() ─
x IRanges::collapse() masks dplyr::collapse()
x Biobase::combine() masks BiocGenerics::combine(), dplyr::combine()
x IRanges::desc() masks dplyr::desc()
x Matrix::expand() masks S4Vectors::expand(), tidyr::expand()
x plotly::filter() masks dplyr::filter(), stats::filter()
x S4Vectors::first() masks dplyr::first()
x widgetTools::funs() masks dplyr::funs()
x dplyr::lag() masks stats::lag()
x purrr::map() masks mclust::map()
x Matrix::pack() masks tidyr::pack()
x BiocGenerics::Position() masks ggplot2::Position(), base::Position()
x purrr::reduce() masks IRanges::reduce()
x S4Vectors::rename() masks plotly::rename(), dplyr::rename()
x AnnotationDbi::select() masks plotly::select(), dplyr::select()
x purrr::simplify() masks clusterProfiler::simplify()
x xgboost::slice() masks IRanges::slice(), plotly::slice(), dplyr::slice()
x Matrix::unpack() masks tidyr::unpack()
ds0 <- readRDS("./ds0.rds")
ds1 <- readRDS("./ds1.rds")
Loading required package: Seurat
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'spatstat':
method from
print.boxx cli
ds2 <- readRDS("./ds2.rds")
分发训练集
# Idents(ds2) <- ds2$conditions
# ds2_AC <- subset(ds2, idents = "AC")
# ds2_PA <- subset(ds2, idents = "PA")
# ds2_AC <- ds2_AC %>% FindNeighbors(dims = 1:20) %>% FindClusters(resolution = 0.1)
# ds2_PA <- ds2_PA %>% FindNeighbors(dims = 1:20) %>% FindClusters(resolution = 0.1)
# umapplot(ds2_AC) + scale_y_continuous(limits = c(-5,15),breaks = NULL) +
# scale_x_continuous(limits = c(-5,15),breaks = NULL)
# umapplot(ds2_PA)+ scale_y_continuous(limits = c(-5,15),breaks = NULL) +
# scale_x_continuous(limits = c(-5,15),breaks = NULL)
#
# AC_markers <- FindAllMarkers(ds2_AC,logfc.threshold = 0.7, min.diff.pct = 0.2)
# # PA_markers <- FindAllMarkers(ds2_PA,logfc.threshold = 0.7, min.diff.pct = 0.2)
# write.csv(AC_markers,"AC_SMC_markers.csv")
ds2_AC <- readRDS("ds2_AC.rds")
ds2_PA <- readRDS("ds2_PA.rds")
umapplot(ds2_AC)
umapplot(ds2_PA)
ds2_AC$Classification <- Idents(ds2_AC)
Idents(ds2_AC) <- ds2_AC$seurat_clusters
ds2_AC <- RenameIdents(ds2_AC,'0' = '3','1' = '1','2' = '0','3' = '2')
Idents(ds2_AC) <- factor(Idents(ds2_AC),levels = c(0,1,2,3))
ds2_AC$seurat_clusters <- Idents(ds2_AC)
ds2_PA$Classification <- Idents(ds2_PA)
Idents(ds2_PA) <- ds2_PA$seurat_clusters
在AC上预训练
ds2_AC$Classification <- Idents(ds2_AC)
Idents(ds2_AC) <- ds2_AC$seurat_clusters
AC_data <- get_data_table(ds2_AC, highvar = F, type = "data")
AC_label <- as.numeric(as.character(Idents(ds2_AC)))
set.seed(7)
index <- c(1:dim(AC_data)[2]) %>% sample(ceiling(0.3*dim(AC_data)[2]), replace = F, prob = NULL)
colnames(AC_data) <- NULL
AC_train_data <- list(data = t(as(AC_data[,-index],"dgCMatrix")), label = AC_label[-index])
AC_test_data <- list(data = t(as(AC_data[,index],"dgCMatrix")), label = AC_label[index])
AC_train <- xgb.DMatrix(data = AC_train_data$data,label = AC_train_data$label)
AC_test <- xgb.DMatrix(data = AC_test_data$data,label = AC_test_data$label)
# xgb_params_train = {
# 'objective':'multi:softmax',
# 'eval_metric':'mlogloss',
# 'num_class':self.numbertrainclasses,
# 'eta':0.2,
# 'max_depth':6,
# 'subsample': 0.6}
# nround = 200
watchlist <- list(train = AC_train, eval = AC_test)
xgb_param <- list(eta = 0.2, max_depth = 6,
subsample = 0.6, num_class = length(table(Idents(ds2_AC))),
objective = "multi:softmax", eval_metric = 'mlogloss')
bst_model <- xgb.train(xgb_param, AC_train, nrounds = 100, watchlist, verbose = 0)
eval_loss <- bst_model[["evaluation_log"]][["eval_mlogloss"]]
plot_ly(data.frame(eval_loss), x = c(1:100), y = eval_loss) %>%
add_trace(type = "scatter", mode = "markers+lines",
marker = list(color = "black", line = list(color = "#1E90FFC7", width = 1)),
line = list(color = "#1E90FF80", width = 2)) %>%
layout(xaxis = list(title = "epoch"),yaxis = list(title = "eval_mlogloss"))
# 特征提取
importance <- xgb.importance(colnames(AC_train), model = bst_model)
head(importance)
xgb.ggplot.importance(head(importance,20), n_clusters = 1) + theme_minimal()
multi_featureplot(head(importance,9)$Feature, ds2_AC)
AC_genes <- head(importance, 500) ##选择top500
write.csv(AC_genes, "./datatable/AC_features.csv", row.names = F)
#混淆矩阵
predict_AC_test <- round(predict(bst_model, newdata = AC_test))
AC_confuse_matrix_test <- table(AC_test_data$label, predict_AC_test, dnn=c("true","pre"))
AC_confuse_matrix_test_prop <- prop.table(AC_confuse_matrix_test, 1)
AC_confuse_matrix_test_prop
confuse_bubblemat(AC_confuse_matrix_test_prop, c("Fibroblast", "SMC1", "Fibromyocyte", "SMC2"), c("Fibroblast", "SMC1", "Fibromyocyte", "SMC2"),"AC_pretrain")
#ROC曲线
xgboost_roc <- pROC::multiclass.roc(AC_test_data$label, predict_AC_test) #多分类ROC
xgboost_roc[["auc"]] #只需要这个值
adjustedRandIndex(AC_test_data$label, predict_AC_test) #分类器性能
在PA上训练
ds2_PA$Classification <- Idents(ds2_PA)
Idents(ds2_PA) <- ds2_PA$seurat_clusters
PA_data <- get_data_table(ds2_PA, highvar = F, type = "data")
PA_label <- as.numeric(as.character(Idents(ds2_PA)))
set.seed(7)
index <- c(1:dim(PA_data)[2]) %>% sample(ceiling(0.3*dim(PA_data)[2]), replace = F, prob = NULL)
colnames(PA_data) <- NULL
PA_train_data <- list(data = t(as(PA_data[,-index],"dgCMatrix")), label = PA_label[-index])
PA_test_data <- list(data = t(as(PA_data[,index],"dgCMatrix")), label = PA_label[index])
PA_train <- xgb.DMatrix(data = PA_train_data$data,label = PA_train_data$label)
PA_test <- xgb.DMatrix(data = PA_test_data$data,label = PA_test_data$label)
watchlist <- list(train = PA_train, eval = PA_test)
xgb_param <- list(eta = 0.2, max_depth = 6,
subsample = 0.6, num_class = length(table(Idents(ds2_PA))),
objective = "multi:softmax", eval_metric = 'mlogloss')
bst_model <- xgb.train(xgb_param, PA_train, nrounds = 100, watchlist, verbose = 0)
eval_loss <- bst_model[["evaluation_log"]][["eval_mlogloss"]]
plot_ly(data.frame(eval_loss), x = c(1:100), y = eval_loss) %>%
add_trace(type = "scatter", mode = "markers+lines",
marker = list(color = "black", line = list(color = "#1E90FFC7", width = 1)),
line = list(color = "#1E90FF80", width = 2)) %>%
layout(xaxis = list(title = "epoch"),yaxis = list(title = "eval_mlogloss"))
# 特征提取
importance <- xgb.importance(colnames(PA_train), model = bst_model)
head(importance)
xgb.ggplot.importance(head(importance,20),n_clusters = 1) + theme_minimal()
multi_featureplot(head(importance,9)$Feature, ds2_PA)
PA_genes <- head(importance, 500) ##选择top500
write.csv(PA_genes, "./datatable/PA_features.csv", row.names = F)
#混淆矩阵
predict_PA_test <- round(predict(bst_model, newdata = PA_test))
PA_confuse_matrix_test <- table(PA_test_data$label, predict_PA_test, dnn=c("true","pre"))
PA_confuse_matrix_test_prop <- prop.table(PA_confuse_matrix_test,1)
PA_confuse_matrix_test_prop
confuse_bubblemat(PA_confuse_matrix_test_prop,c("Fibromyocyte", "SMC1", "SMC2"),c("Fibromyocyte", "SMC1", "SMC2"),"PA_pretrain")
#ROC曲线
xgboost_roc <- pROC::multiclass.roc(PA_test_data$label, predict_PA_test) #多分类ROC
xgboost_roc[["auc"]]
adjustedRandIndex(PA_test_data$label, predict_PA_test) #PA分类器性能
选择特征common genes of top 500
使用所有来自PA的细胞训练分类器
应用在AC上,计算ARI
selected_features <- read.csv("./datatable/selected_features.csv", stringsAsFactors = F)
selected_features <- selected_features$x
PA_data <- get_data_table(ds2_PA, highvar = F, type = "data")
PA_data <- PA_data[selected_features,]
PA_label <- as.numeric(as.character(Idents(ds2_PA)))
colnames(PA_data) <- NULL
PA_train_data <- list(data = t(as(PA_data,"dgCMatrix")), label = PA_label)
PA_train <- xgb.DMatrix(data = PA_train_data$data,label = PA_train_data$label)
xgb_param <- list(eta = 0.2, max_depth = 6,
subsample = 0.6, num_class = length(table(Idents(ds2_PA))),
objective = "multi:softmax", eval_metric = 'mlogloss')
bst_model <- xgb.train(xgb_param, PA_train, nrounds = 100, verbose = 0)
# 特征提取
importance <- xgb.importance(colnames(PA_train), model = bst_model)
head(importance)
xgb.ggplot.importance(head(importance,20),n_clusters = 1) + theme_bw()

write.csv(importance, "./datatable/PAtrain_features.csv", row.names = F)
应用到AC上
AC_data <- get_data_table(ds2_AC, highvar = F, type = "data")
AC_data <- AC_data[selected_features,]
AC_label <- as.numeric(as.character(Idents(ds2_AC)))
colnames(AC_data) <- NULL
AC_test_data <- list(data = t(as(AC_data,"dgCMatrix")), label = AC_label)
AC_test <- xgb.DMatrix(data = AC_test_data$data,label = AC_test_data$label)
#计算混淆矩阵
predict_AC_test <- round(predict(bst_model, newdata = AC_test))
AC_confuse_matrix_test <- table(AC_test_data$label, predict_AC_test, dnn=c("true","pre"))
AC_confuse_matrix_test_prop <- prop.table(AC_confuse_matrix_test,1)
AC_confuse_matrix_test_prop #分析发育轨迹
pre
true 0 1 2
0 0.996726678 0.003273322 0.000000000
1 0.825876663 0.170495768 0.003627570
2 0.435185185 0.509259259 0.055555556
3 0.002762431 0.069060773 0.928176796
confuse_bubblemat(AC_confuse_matrix_test_prop,c("Fibroblast", "SMC1", "Fibromyocyte", "SMC2"), c("Fibromyocyte", "SMC1", "SMC2"), "PAtoAC")
#ROC曲线
xgboost_roc <- pROC::multiclass.roc(AC_test_data$label, predict_AC_test) #多分类ROC
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
xgboost_roc[["auc"]]
Multi-class area under the curve: 0.8342
# 计算ARI
adjustedRandIndex(predict_AC_test, AC_test_data$label)
[1] 0.3147662
sankey plot
PA -> AC
sankey_plot(AC_confuse_matrix_test, label1 = c("Fibroblast", "SMC1", "SMC2"), label2 = c("Fibroblast", "SMC1", "Fibromyocyte", "SMC2"), session = "PA -> AC")
umapplot(ds2_AC)

umapplot(ds2_PA)

# umapplot(ds2,split.by = "conditions")
#把结果投射回umap
Idents(ds2_AC) <- predict_AC_test
ds2_AC$predict_AC_test <- predict_AC_test
umapplot(ds2_AC,group.by = "predict_AC_test")

Idents(ds2_AC) <- ds2_AC$seurat_clusters
反着做
选择特征common genes of top 500
使用所有来自AC的细胞训练分类器

应用在PA上,计算ARI
PA_data <- get_data_table(ds2_PA, highvar = F, type = "data")
PA_data <- PA_data[selected_features,]
PA_label <- as.numeric(as.character(Idents(ds2_PA)))
colnames(PA_data) <- NULL
PA_test_data <- list(data = t(as(PA_data,"dgCMatrix")), label = PA_label)
PA_test <- xgb.DMatrix(data = PA_test_data$data,label = PA_test_data$label)
#计算混淆矩阵
predict_PA_test <- round(predict(bst_model2, newdata = PA_test))
PA_confuse_matrix_test <- table(PA_test_data$label, predict_PA_test, dnn=c("true","pre"))
PA_confuse_matrix_test_prop <- prop.table(PA_confuse_matrix_test,1)
PA_confuse_matrix_test_prop #分析发育轨迹
confuse_bubblemat(PA_confuse_matrix_test_prop,c("Fibromyocyte", "SMC1", "SMC2"),c("Fibroblast", "SMC1", "Fibromyocyte", "SMC2"),session = "ACtoPA")
# 计算ARI
adjustedRandIndex(predict_PA_test, PA_test_data$label)
把结果投射回umap
Idents(ds2_PA) <- predict_PA_test
ds2_PA$predict_PA_test <- predict_PA_test
umapplot(ds2_PA,group.by = "predict_PA_test")
Idents(ds2_PA) <- ds2_PA$seurat_clusters
sankey plot
labels <- c("Fibroblast", "SMC1", "Fibromyocyte", "SMC2")
labels2 <- c("Fibromyocyte", "SMC1", "SMC2")
sankey_plot(PA_confuse_matrix_test,labels,labels2,session = "AC -> PA")
varify 部分
病变程度量化 # 数据集CA_dataset1 ## ds2全体训练
Idents(ds2) <- ds2$seurat_clusters
ds2_data <- get_data_table(ds2, highvar = F, type = "data")
ds2_label <- as.numeric(as.character(Idents(ds2)))
index <- c(1:dim(ds2_data)[2]) %>% sample(ceiling(0.3*dim(ds2_data)[2]), replace = F, prob = NULL)
colnames(ds2_data) <- NULL
ds2_train_data <- list(data = t(as(ds2_data[,-index],"dgCMatrix")), label = ds2_label[-index])
ds2_test_data <- list(data = t(as(ds2_data[,index],"dgCMatrix")), label = ds2_label[index])
ds2_train <- xgb.DMatrix(data = ds2_train_data$data,label = ds2_train_data$label)
ds2_test <- xgb.DMatrix(data = ds2_test_data$data,label = ds2_test_data$label)
watchlist <- list(train = ds2_train, eval = ds2_test)
xgb_param <- list(eta = 0.2, max_depth = 6,
subsample = 0.6, num_class = length(table(Idents(ds2))),
objective = "multi:softmax", eval_metric = 'mlogloss')
bst_model <- xgb.train(xgb_param, ds2_train, nrounds = 100, watchlist, verbose = 0)
eval_loss <- bst_model[["evaluation_log"]][["eval_mlogloss"]]
plot_ly(data.frame(eval_loss), x = c(1:100), y = eval_loss) %>%
add_trace(type = "scatter", mode = "markers+lines",
marker = list(color = "black", line = list(color = "#1E90FFC7", width = 1)),
line = list(color = "#1E90FF80", width = 2)) %>%
layout(xaxis = list(title = "epoch"),yaxis = list(title = "eval_mlogloss"))
# 特征提取
importance <- xgb.importance(colnames(ds2_train), model = bst_model)
head(importance)
xgb.ggplot.importance(head(importance,20), n_clusters = 1) + theme_minimal()
multi_featureplot(head(importance,9)$Feature, ds2)
ds2_genes <- head(importance, 500) ##选择top500
write.csv(ds2_genes, "./datatable/ds2_features.csv", row.names = F)
predict_ds2_test <- round(predict(bst_model, newdata = ds2_test)) %>%
#混淆矩阵
ds2_confuse_matrix_test <- table(ds2_test_data$label, predict_ds2_test, dnn=c("true","pre"))
ds2_confuse_matrix_test_prop <- prop.table(ds2_confuse_matrix_test, 1)
ds2_confuse_matrix_test_prop
x <- c(0:4)
y <- c(0:4)
confuse_bubblemat(ds2_confuse_matrix_test_prop,x,y,"ds2_train")
#ROC曲线
xgboost_roc <- pROC::multiclass.roc(ds2_test_data$label, predict_ds2_test) #多分类ROC
xgboost_roc[["auc"]] #只需要这个值
adjustedRandIndex(ds2_test_data$label, predict_ds2_test) #分类器性能
temp <- get_data_table(ds1, highvar = T, type = "data")
ds1_data <- matrix(data=0, nrow = dim(ds2_data)[1], ncol = length(colnames(temp)),
byrow = FALSE, dimnames = list(rownames(ds2_data),colnames(temp)))
for(i in intersect(rownames(ds2_data), rownames(temp))){
ds1_data[i,] <- temp[i,]
}
rm(temp)
ds1_label <- as.numeric(as.character(Idents(ds1)))
colnames(ds1_data) <- NULL
ds1_test_data <- list(data = t(as(ds1_data,"dgCMatrix")), label = ds1_label)
ds1_test <- xgb.DMatrix(data = ds1_test_data$data,label = ds1_test_data$label)
#预测结果
predict_ds1_test <- round(predict(bst_model, newdata = ds1_test))
#计算混淆矩阵
ds1_data_confuse_matrix_test <- table(ds1_test_data$label, predict_ds1_test, dnn=c("true","pre"))
ds1_data_confuse_matrix_test_prop <- prop.table(ds1_data_confuse_matrix_test,1)
#绘制混淆矩阵
x <- c("Fibromyocyte", "SMC1", "SMC2")
y <- c("Fibroblast", "SMC1", "Fibromyocyte", "SMC2")
confuse_bubblemat(ds1_data_confuse_matrix_test_prop,x,y,"ds2tods1")
ds1_data_confuse_matrix_test
ds1_data_confuse_matrix_test_prop #分析发育轨迹
#ROC曲线
xgboost_roc <- pROC::multiclass.roc(ds1_test_data$label, predict_ds1_test) #多分类ROC
xgboost_roc[["auc"]]
# 计算ARI
adjustedRandIndex(predict_ds1_test, ds1_test_data$label)
投射回umap
Idents(ds1) <- predict_ds1_test
ds1$predict_ds1_test <- predict_ds1_test
umapplot(ds1,group.by = "predict_ds1_test")
Idents(ds1) <- ds1$seurat_clusters
冠状动脉数据集
ds0 <- ds0 %>% FindNeighbors(dims = 1:20) %>% FindClusters(resolution = 0.1)
umapplot(ds0)
f("TAGLN",ds0)
# ds0_markers <- FindAllMarkers(ds0,logfc.threshold = 0.7, min.diff.pct = 0.2)
selected_features <- AC_genes$Feature
temp <- get_data_table(ds0, highvar = F, type = "data")
ds0_data <- matrix(data=0, nrow = length(selected_features),
ncol = length(colnames(temp)), byrow = FALSE,
dimnames = list(selected_features,colnames(temp)))
for(i in intersect(selected_features,rownames(temp))){
ds0_data[i,] <- temp[i,]
}
rm(temp)
ds0_label <- as.numeric(as.character(Idents(ds0)))
colnames(ds0_data) <- NULL
ds0_test_data <- list(data = t(as(ds0_data,"dgCMatrix")), label = ds0_label)
ds0_test <- xgb.DMatrix(data = ds0_test_data$data,label = ds0_test_data$label)
#计算混淆矩阵
predict_ds0_test <- round(predict(bst_model, newdata = ds0_test))
ds0_data_confuse_matrix_test <- table(ds0_test_data$label, predict_ds0_test, dnn=c("true","pre"))
ds0_data_confuse_matrix_test_prop <- prop.table(ds0_data_confuse_matrix_test,1)
x <- c("ds0_0", "ds0_1", "ds0_2", "ds0_3", "ds0_4")
y <- c("AC_0", "AC_1", "AC_2")
prop <- as.numeric(ds0_data_confuse_matrix_test_prop)
data <- expand.grid(x = x, y = y) %>% bind_cols(prop = prop)
plot <- ggplot(data, aes(x = x, y = y, colour = prop, size = prop)) +
geom_point()+
scale_size_continuous(range = c(0, 10)) +
labs(x = "clusters", y = "inferred from") + theme_bw()
ggsave("./plots/ACmodel_humancor.png", plot = plot, device = png, width = 5,height = 4)
ds0_data_confuse_matrix_test
ds0_data_confuse_matrix_test_prop #分析发育轨迹
#ROC曲线
xgboost_roc <- pROC::multiclass.roc(ds0_test_data$label, predict_ds0_test) #多分类ROC
# 计算ARI
adjustedRandIndex(predict_ds0_test, ds0_test_data$label)
labels <- lapply(levels(Idents(ds2_AC)), paste0, "_AC") %>% as.character()
labels2 <- lapply(levels(Idents(ds0)), paste0, "_ds0") %>% as.character()
sources <- rep(0:(length(labels)-1), each = length(labels2)) #注意这里的each和times的区别
colors <- rep(colors_list[1:length(labels)], each = length(labels2))
targets <- rep(length(labels)+0:(length(labels2)-1), times = length(labels))
plot_ly(type = "sankey", orientation = "h",
node = list(
label = c(labels,labels2),
color = colors_list, pad = 15, thickness = 30,
line = list(
color = "black",
width = 1)),
link = list(
source = sources,
target = targets,
value = as.numeric(ds0_data_confuse_matrix_test),
color = colors
))
# load("./init.RData")
multi_featureplot(head(importance2,9)$Feature, ds2_AC)
multi_featureplot(head(importance2,9)$Feature, ds0)
multi_featureplot(head(importance2,9)$Feature, ds1)
f("MYH11", ds2_AC)
umapplot(ds0)
淋巴细胞
Idents(lym_ds2) <- lym_ds2$conditions
lym_ds2_AC <- subset(lym_ds2, idents = "AC")
lym_ds2_PA <- subset(lym_ds2, idents = "PA")
lym_ds2_AC <- lym_ds2_AC %>% FindNeighbors(dims = 1:20) %>% FindClusters(resolution = 0.2)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 2990
Number of edges: 98189
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9128
Number of communities: 5
Elapsed time: 0 seconds
umapplot(lym_ds2_AC)

lym_ds2_PA <- lym_ds2_PA %>% FindNeighbors(dims = 1:20) %>% FindClusters(resolution = 0.2)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 13746
Number of edges: 456548
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9116
Number of communities: 6
Elapsed time: 2 seconds
umapplot(lym_ds2_PA)

用PA的lym训练
lym_PA_data <- get_data_table(lym_ds2_PA, highvar = F, type = "data")
lym_PA_label <- as.numeric(as.character(Idents(lym_ds2_PA)))
set.seed(7)
index <- c(1:dim(lym_PA_data)[2]) %>% sample(ceiling(0.3*dim(lym_PA_data)[2]), replace = F, prob = NULL)
colnames(lym_PA_data) <- NULL
lym_PA_train_data <- list(data = t(as(lym_PA_data[,-index],"dgCMatrix")), label = lym_PA_label[-index])
lym_PA_test_data <- list(data = t(as(lym_PA_data[,index],"dgCMatrix")), label = lym_PA_label[index])
lym_PA_train <- xgb.DMatrix(data = lym_PA_train_data$data,label = lym_PA_train_data$label)
lym_PA_test <- xgb.DMatrix(data = lym_PA_test_data$data,label = lym_PA_test_data$label)
watchlist <- list(train = lym_PA_train, eval = lym_PA_test)
xgb_param <- list(eta = 0.2, max_depth = 6,
subsample = 0.6, num_class = length(table(Idents(lym_ds2_PA))),
objective = "multi:softmax", eval_metric = 'mlogloss')
bst_model <- xgb.train(xgb_param, lym_PA_train, nrounds = 100, watchlist, verbose = 0)

用AC的lym验证
labels <- lapply(levels(Idents(lym_ds2_PA)), paste0, "_lymPA") %>% as.character()
labels2 <- lapply(levels(Idents(lym_ds2_AC)), paste0, "_lymAC") %>% as.character()
sources <- rep(0:5, each = 5) #注意这里的each和times的区别
colors <- rep(colors_list[1:6], each = 5)
targets <- rep(6:10, times = 6)
plot_ly(type = "sankey", orientation = "h",
node = list(
label = c(labels,labels2),
color = colors_list, pad = 15, thickness = 30,
line = list(
color = "black",
width = 1)),
link = list(
source = sources,
target = targets,
value = as.numeric(lym_AC_confuse_matrix_test),
color = colors
))
umapplot(lym_ds2_AC)
umapplot(lym_ds2_PA)
functions set
sankey_plot <- function(confuse_matrix, label1, label2, session = "session")
{
sources <- rep(0:(length(label1)-1), each = length(label2)) #注意这里的each和times的区别
colors <- rep(aero_colors_list[1:length(label1)], each = length(label2))
targets <- rep(length(label1)+0:(length(label2)-1), times = length(label1))
plot_ly(type = "sankey", orientation = "h",
node = list(
label = c(label1,label2),
color = colors_list, pad = 15, thickness = 30,
line = list(color = "black", width = 1)),
link = list(
source = sources, target = targets,
value = as.numeric(confuse_matrix),
color = colors
)) %>% layout(title=session, font=list(family = "Arial",size = 20, color = 'black'))
}
confuse_bubblemat <- function(confuse_matrix_prop, label1, label2, session = "session")
{
prop <- as.numeric(confuse_matrix_prop)
data <- expand.grid(x = label1, y = label2) %>% bind_cols(prop = prop)
plot <- ggplot(data, aes(x = x, y = y, colour = prop, size = prop)) +
geom_point()+
scale_size_continuous(range = c(0, 10)) +
labs(x = "clusters", y = "inferred from") + theme_bw()
ggsave(paste0(session, ".svg"), plot = plot, device = svg, width = 5,height = 4)
}
## 返回最大的概率对应的index
func <- function(s, ident)
{
if(max(s)>1.2/length(ident))
return(ident[which(s == max(s))])
else
return("unassigned")
}
aero_colors_list <- as.character(lapply(colors_list, paste0, "A0")) #透明化颜色
Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAoKYGBge3J9CmxpYnJhcnkoeGdib29zdCkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkobWNsdXN0KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7cn0KZHMwIDwtIHJlYWRSRFMoIi4vZHMwLnJkcyIpCmRzMSA8LSByZWFkUkRTKCIuL2RzMS5yZHMiKQpkczIgPC0gcmVhZFJEUygiLi9kczIucmRzIikKYGBgCgojIOWIhuWPkeiuree7g+mbhgpgYGB7cn0KIyBJZGVudHMoZHMyKSA8LSBkczIkY29uZGl0aW9ucwojIGRzMl9BQyA8LSBzdWJzZXQoZHMyLCBpZGVudHMgPSAiQUMiKQojIGRzMl9QQSA8LSBzdWJzZXQoZHMyLCBpZGVudHMgPSAiUEEiKQojIGRzMl9BQyA8LSBkczJfQUMgJT4lIEZpbmROZWlnaGJvcnMoZGltcyA9IDE6MjApICU+JSBGaW5kQ2x1c3RlcnMocmVzb2x1dGlvbiA9IDAuMSkKIyBkczJfUEEgPC0gZHMyX1BBICU+JSBGaW5kTmVpZ2hib3JzKGRpbXMgPSAxOjIwKSAlPiUgRmluZENsdXN0ZXJzKHJlc29sdXRpb24gPSAwLjEpCiMgdW1hcHBsb3QoZHMyX0FDKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC01LDE1KSxicmVha3MgPSBOVUxMKSArCiMgICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygtNSwxNSksYnJlYWtzID0gTlVMTCkKIyB1bWFwcGxvdChkczJfUEEpKyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygtNSwxNSksYnJlYWtzID0gTlVMTCkgKwojICAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTUsMTUpLGJyZWFrcyA9IE5VTEwpCiMgCiMgQUNfbWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhkczJfQUMsbG9nZmMudGhyZXNob2xkID0gMC43LCBtaW4uZGlmZi5wY3QgPSAwLjIpCiMgIyBQQV9tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKGRzMl9QQSxsb2dmYy50aHJlc2hvbGQgPSAwLjcsIG1pbi5kaWZmLnBjdCA9IDAuMikKIyB3cml0ZS5jc3YoQUNfbWFya2VycywiQUNfU01DX21hcmtlcnMuY3N2IikKCmRzMl9BQyA8LSByZWFkUkRTKCJkczJfQUMucmRzIikKZHMyX1BBIDwtIHJlYWRSRFMoImRzMl9QQS5yZHMiKQoKdW1hcHBsb3QoZHMyX0FDKQp1bWFwcGxvdChkczJfUEEpCgpkczJfQUMkQ2xhc3NpZmljYXRpb24gPC0gSWRlbnRzKGRzMl9BQykKSWRlbnRzKGRzMl9BQykgPC0gZHMyX0FDJHNldXJhdF9jbHVzdGVycwpkczJfQUMgPC0gUmVuYW1lSWRlbnRzKGRzMl9BQywnMCcgPSAnMycsJzEnID0gJzEnLCcyJyA9ICcwJywnMycgPSAnMicpCklkZW50cyhkczJfQUMpIDwtIGZhY3RvcihJZGVudHMoZHMyX0FDKSxsZXZlbHMgPSBjKDAsMSwyLDMpKQpkczJfQUMkc2V1cmF0X2NsdXN0ZXJzIDwtIElkZW50cyhkczJfQUMpCgpkczJfUEEkQ2xhc3NpZmljYXRpb24gPC0gSWRlbnRzKGRzMl9QQSkKSWRlbnRzKGRzMl9QQSkgPC0gZHMyX1BBJHNldXJhdF9jbHVzdGVycwoKCmBgYAoKCgoKIyMg5ZyoQUPkuIrpooTorq3nu4MKYGBge3J9CmRzMl9BQyRDbGFzc2lmaWNhdGlvbiA8LSBJZGVudHMoZHMyX0FDKQpJZGVudHMoZHMyX0FDKSA8LSBkczJfQUMkc2V1cmF0X2NsdXN0ZXJzCkFDX2RhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyX0FDLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKQUNfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGRzMl9BQykpKQoKc2V0LnNlZWQoNykKaW5kZXggPC0gYygxOmRpbShBQ19kYXRhKVsyXSkgJT4lIHNhbXBsZShjZWlsaW5nKDAuMypkaW0oQUNfZGF0YSlbMl0pLCByZXBsYWNlID0gRiwgcHJvYiA9IE5VTEwpCgpjb2xuYW1lcyhBQ19kYXRhKSA8LSBOVUxMCgpBQ190cmFpbl9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoQUNfZGF0YVssLWluZGV4XSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IEFDX2xhYmVsWy1pbmRleF0pCkFDX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKEFDX2RhdGFbLGluZGV4XSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IEFDX2xhYmVsW2luZGV4XSkKCkFDX3RyYWluIDwtIHhnYi5ETWF0cml4KGRhdGEgPSBBQ190cmFpbl9kYXRhJGRhdGEsbGFiZWwgPSBBQ190cmFpbl9kYXRhJGxhYmVsKQpBQ190ZXN0IDwtIHhnYi5ETWF0cml4KGRhdGEgPSBBQ190ZXN0X2RhdGEkZGF0YSxsYWJlbCA9IEFDX3Rlc3RfZGF0YSRsYWJlbCkKCiMgeGdiX3BhcmFtc190cmFpbiA9IHsKIyAgICAgJ29iamVjdGl2ZSc6J211bHRpOnNvZnRtYXgnLAojICAgICAnZXZhbF9tZXRyaWMnOidtbG9nbG9zcycsCiMgICAgICdudW1fY2xhc3MnOnNlbGYubnVtYmVydHJhaW5jbGFzc2VzLAojICAgICAnZXRhJzowLjIsCiMgICAgICdtYXhfZGVwdGgnOjYsCiMgICAgICdzdWJzYW1wbGUnOiAwLjZ9CiMgbnJvdW5kID0gMjAwCgp3YXRjaGxpc3QgPC0gbGlzdCh0cmFpbiA9IEFDX3RyYWluLCBldmFsID0gQUNfdGVzdCkKeGdiX3BhcmFtIDwtIGxpc3QoZXRhID0gMC4yLCBtYXhfZGVwdGggPSA2LCAKICAgICAgICAgICAgICAgICAgc3Vic2FtcGxlID0gMC42LCAgbnVtX2NsYXNzID0gbGVuZ3RoKHRhYmxlKElkZW50cyhkczJfQUMpKSksCiAgICAgICAgICAgICAgICAgIG9iamVjdGl2ZSA9ICJtdWx0aTpzb2Z0bWF4IiwgZXZhbF9tZXRyaWMgPSAnbWxvZ2xvc3MnKQoKYnN0X21vZGVsIDwtIHhnYi50cmFpbih4Z2JfcGFyYW0sIEFDX3RyYWluLCBucm91bmRzID0gMTAwLCB3YXRjaGxpc3QsIHZlcmJvc2UgPSAwKQoKZXZhbF9sb3NzIDwtIGJzdF9tb2RlbFtbImV2YWx1YXRpb25fbG9nIl1dW1siZXZhbF9tbG9nbG9zcyJdXQpwbG90X2x5KGRhdGEuZnJhbWUoZXZhbF9sb3NzKSwgeCA9IGMoMToxMDApLCB5ID0gZXZhbF9sb3NzKSAlPiUgCiAgYWRkX3RyYWNlKHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycytsaW5lcyIsIAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsYWNrIiwgbGluZSA9IGxpc3QoY29sb3IgPSAiIzFFOTBGRkM3Iiwgd2lkdGggPSAxKSksCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gIiMxRTkwRkY4MCIsIHdpZHRoID0gMikpICU+JSAKICBsYXlvdXQoeGF4aXMgPSBsaXN0KHRpdGxlID0gImVwb2NoIikseWF4aXMgPSBsaXN0KHRpdGxlID0gImV2YWxfbWxvZ2xvc3MiKSkKCmBgYAoKYGBge3IsZmlnLmhlaWdodD00LGZpZy53aWR0aD00fQojIOeJueW+geaPkOWPlgppbXBvcnRhbmNlIDwtIHhnYi5pbXBvcnRhbmNlKGNvbG5hbWVzKEFDX3RyYWluKSwgbW9kZWwgPSBic3RfbW9kZWwpCmhlYWQoaW1wb3J0YW5jZSkKeGdiLmdncGxvdC5pbXBvcnRhbmNlKGhlYWQoaW1wb3J0YW5jZSwyMCksIG5fY2x1c3RlcnMgPSAxKSArIHRoZW1lX21pbmltYWwoKQoKbXVsdGlfZmVhdHVyZXBsb3QoaGVhZChpbXBvcnRhbmNlLDkpJEZlYXR1cmUsIGRzMl9BQykgCkFDX2dlbmVzIDwtIGhlYWQoaW1wb3J0YW5jZSwgNTAwKSAjI+mAieaLqXRvcDUwMAoKd3JpdGUuY3N2KEFDX2dlbmVzLCAiLi9kYXRhdGFibGUvQUNfZmVhdHVyZXMuY3N2Iiwgcm93Lm5hbWVzID0gRikKCiPmt7fmt4bnn6npmLUKcHJlZGljdF9BQ190ZXN0IDwtIHJvdW5kKHByZWRpY3QoYnN0X21vZGVsLCBuZXdkYXRhID0gQUNfdGVzdCkpCgpBQ19jb25mdXNlX21hdHJpeF90ZXN0IDwtIHRhYmxlKEFDX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9BQ190ZXN0LCBkbm49YygidHJ1ZSIsInByZSIpKQpBQ19jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgPC0gcHJvcC50YWJsZShBQ19jb25mdXNlX21hdHJpeF90ZXN0LCAxKQpBQ19jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AKCmNvbmZ1c2VfYnViYmxlbWF0KEFDX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCwgYygiRmlicm9ibGFzdCIsICJTTUMxIiwgIkZpYnJvbXlvY3l0ZSIsICJTTUMyIiksIGMoIkZpYnJvYmxhc3QiLCAiU01DMSIsICJGaWJyb215b2N5dGUiLCAiU01DMiIpLCJBQ19wcmV0cmFpbiIpCgojUk9D5puy57q/CnhnYm9vc3Rfcm9jIDwtIHBST0M6Om11bHRpY2xhc3Mucm9jKEFDX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9BQ190ZXN0KSAj5aSa5YiG57G7Uk9DCnhnYm9vc3Rfcm9jW1siYXVjIl1dICPlj6rpnIDopoHov5nkuKrlgLwKYWRqdXN0ZWRSYW5kSW5kZXgoQUNfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X0FDX3Rlc3QpICPliIbnsbvlmajmgKfog70KYGBgCgoKIyMg5ZyoUEHkuIrorq3nu4MKYGBge3J9CmRzMl9QQSRDbGFzc2lmaWNhdGlvbiA8LSBJZGVudHMoZHMyX1BBKQpJZGVudHMoZHMyX1BBKSA8LSBkczJfUEEkc2V1cmF0X2NsdXN0ZXJzCgpQQV9kYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMl9QQSwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpClBBX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczJfUEEpKSkKc2V0LnNlZWQoNykKaW5kZXggPC0gYygxOmRpbShQQV9kYXRhKVsyXSkgJT4lIHNhbXBsZShjZWlsaW5nKDAuMypkaW0oUEFfZGF0YSlbMl0pLCByZXBsYWNlID0gRiwgcHJvYiA9IE5VTEwpCmNvbG5hbWVzKFBBX2RhdGEpIDwtIE5VTEwKClBBX3RyYWluX2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhQQV9kYXRhWywtaW5kZXhdLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gUEFfbGFiZWxbLWluZGV4XSkKUEFfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoUEFfZGF0YVssaW5kZXhdLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gUEFfbGFiZWxbaW5kZXhdKQoKUEFfdHJhaW4gPC0geGdiLkRNYXRyaXgoZGF0YSA9IFBBX3RyYWluX2RhdGEkZGF0YSxsYWJlbCA9IFBBX3RyYWluX2RhdGEkbGFiZWwpClBBX3Rlc3QgPC0geGdiLkRNYXRyaXgoZGF0YSA9IFBBX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gUEFfdGVzdF9kYXRhJGxhYmVsKQoKd2F0Y2hsaXN0IDwtIGxpc3QodHJhaW4gPSBQQV90cmFpbiwgZXZhbCA9IFBBX3Rlc3QpCnhnYl9wYXJhbSA8LSBsaXN0KGV0YSA9IDAuMiwgbWF4X2RlcHRoID0gNiwgCiAgICAgICAgICAgICAgICAgIHN1YnNhbXBsZSA9IDAuNiwgIG51bV9jbGFzcyA9IGxlbmd0aCh0YWJsZShJZGVudHMoZHMyX1BBKSkpLAogICAgICAgICAgICAgICAgICBvYmplY3RpdmUgPSAibXVsdGk6c29mdG1heCIsIGV2YWxfbWV0cmljID0gJ21sb2dsb3NzJykKYnN0X21vZGVsIDwtIHhnYi50cmFpbih4Z2JfcGFyYW0sIFBBX3RyYWluLCBucm91bmRzID0gMTAwLCB3YXRjaGxpc3QsIHZlcmJvc2UgPSAwKQpldmFsX2xvc3MgPC0gYnN0X21vZGVsW1siZXZhbHVhdGlvbl9sb2ciXV1bWyJldmFsX21sb2dsb3NzIl1dCnBsb3RfbHkoZGF0YS5mcmFtZShldmFsX2xvc3MpLCB4ID0gYygxOjEwMCksIHkgPSBldmFsX2xvc3MpICU+JSAKICBhZGRfdHJhY2UodHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzK2xpbmVzIiwgCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiYmxhY2siLCBsaW5lID0gbGlzdChjb2xvciA9ICIjMUU5MEZGQzciLCB3aWR0aCA9IDEpKSwKICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiIzFFOTBGRjgwIiwgd2lkdGggPSAyKSkgJT4lIAogIGxheW91dCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiZXBvY2giKSx5YXhpcyA9IGxpc3QodGl0bGUgPSAiZXZhbF9tbG9nbG9zcyIpKQpgYGAKCmBgYHtyLGZpZy5oZWlnaHQ9NCxmaWcud2lkdGg9NH0KIyDnibnlvoHmj5Dlj5YKaW1wb3J0YW5jZSA8LSB4Z2IuaW1wb3J0YW5jZShjb2xuYW1lcyhQQV90cmFpbiksIG1vZGVsID0gYnN0X21vZGVsKQpoZWFkKGltcG9ydGFuY2UpCnhnYi5nZ3Bsb3QuaW1wb3J0YW5jZShoZWFkKGltcG9ydGFuY2UsMjApLG5fY2x1c3RlcnMgPSAxKSArIHRoZW1lX21pbmltYWwoKQoKbXVsdGlfZmVhdHVyZXBsb3QoaGVhZChpbXBvcnRhbmNlLDkpJEZlYXR1cmUsIGRzMl9QQSkKUEFfZ2VuZXMgPC0gaGVhZChpbXBvcnRhbmNlLCA1MDApICMj6YCJ5oupdG9wNTAwCndyaXRlLmNzdihQQV9nZW5lcywgIi4vZGF0YXRhYmxlL1BBX2ZlYXR1cmVzLmNzdiIsIHJvdy5uYW1lcyA9IEYpCgoj5re35reG55+p6Zi1CnByZWRpY3RfUEFfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IFBBX3Rlc3QpKQoKUEFfY29uZnVzZV9tYXRyaXhfdGVzdCA8LSB0YWJsZShQQV90ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfUEFfdGVzdCwgZG5uPWMoInRydWUiLCJwcmUiKSkKUEFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wIDwtIHByb3AudGFibGUoUEFfY29uZnVzZV9tYXRyaXhfdGVzdCwxKQpQQV9jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AKCmNvbmZ1c2VfYnViYmxlbWF0KFBBX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCxjKCJGaWJyb215b2N5dGUiLCAiU01DMSIsICJTTUMyIiksYygiRmlicm9teW9jeXRlIiwgIlNNQzEiLCAiU01DMiIpLCJQQV9wcmV0cmFpbiIpCgojUk9D5puy57q/Cgp4Z2Jvb3N0X3JvYyA8LSBwUk9DOjptdWx0aWNsYXNzLnJvYyhQQV90ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfUEFfdGVzdCkgI+WkmuWIhuexu1JPQwp4Z2Jvb3N0X3JvY1tbImF1YyJdXQphZGp1c3RlZFJhbmRJbmRleChQQV90ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfUEFfdGVzdCkgI1BB5YiG57G75Zmo5oCn6IO9CmBgYAoKCiMjIOmAieaLqeeJueW+gWNvbW1vbiBnZW5lcyBvZiB0b3AgNTAwCiMjIOS9v+eUqOaJgOacieadpeiHqlBB55qE57uG6IOe6K6t57uD5YiG57G75ZmoCiMjIOW6lOeUqOWcqEFD5LiK77yM6K6h566XQVJJCmBgYHtyLGZpZy5oZWlnaHQ9NCxmaWcud2lkdGg9NH0Kc2VsZWN0ZWRfZmVhdHVyZXMgPC0gaW50ZXJzZWN0KFBBX2dlbmVzJEZlYXR1cmUsIEFDX2dlbmVzJEZlYXR1cmUpCndyaXRlLmNzdihzZWxlY3RlZF9mZWF0dXJlcywgIi4vZGF0YXRhYmxlL3NlbGVjdGVkX2ZlYXR1cmVzLmNzdiIsIHJvdy5uYW1lcyA9IEYpCgpzZWxlY3RlZF9mZWF0dXJlcyA8LSByZWFkLmNzdigiLi9kYXRhdGFibGUvc2VsZWN0ZWRfZmVhdHVyZXMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnNlbGVjdGVkX2ZlYXR1cmVzIDwtIHNlbGVjdGVkX2ZlYXR1cmVzJHgKUEFfZGF0YSA8LSBnZXRfZGF0YV90YWJsZShkczJfUEEsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpQQV9kYXRhIDwtIFBBX2RhdGFbc2VsZWN0ZWRfZmVhdHVyZXMsXQpQQV9sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMoZHMyX1BBKSkpCmNvbG5hbWVzKFBBX2RhdGEpIDwtIE5VTEwKClBBX3RyYWluX2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhQQV9kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gUEFfbGFiZWwpClBBX3RyYWluIDwtIHhnYi5ETWF0cml4KGRhdGEgPSBQQV90cmFpbl9kYXRhJGRhdGEsbGFiZWwgPSBQQV90cmFpbl9kYXRhJGxhYmVsKQp4Z2JfcGFyYW0gPC0gbGlzdChldGEgPSAwLjIsIG1heF9kZXB0aCA9IDYsIAogICAgICAgICAgICAgICAgICBzdWJzYW1wbGUgPSAwLjYsICBudW1fY2xhc3MgPSBsZW5ndGgodGFibGUoSWRlbnRzKGRzMl9QQSkpKSwKICAgICAgICAgICAgICAgICAgb2JqZWN0aXZlID0gIm11bHRpOnNvZnRtYXgiLCBldmFsX21ldHJpYyA9ICdtbG9nbG9zcycpCgpic3RfbW9kZWwgPC0geGdiLnRyYWluKHhnYl9wYXJhbSwgUEFfdHJhaW4sIG5yb3VuZHMgPSAxMDAsIHZlcmJvc2UgPSAwKQoKIyDnibnlvoHmj5Dlj5YKaW1wb3J0YW5jZSA8LSB4Z2IuaW1wb3J0YW5jZShjb2xuYW1lcyhQQV90cmFpbiksIG1vZGVsID0gYnN0X21vZGVsKQpoZWFkKGltcG9ydGFuY2UpCnhnYi5nZ3Bsb3QuaW1wb3J0YW5jZShoZWFkKGltcG9ydGFuY2UsMjApLG5fY2x1c3RlcnMgPSAxKSArIHRoZW1lX2J3KCkKd3JpdGUuY3N2KGltcG9ydGFuY2UsICIuL2RhdGF0YWJsZS9QQXRyYWluX2ZlYXR1cmVzLmNzdiIsIHJvdy5uYW1lcyA9IEYpCgojIG11bHRpX2ZlYXR1cmVwbG90KGhlYWQoaW1wb3J0YW5jZSw5KSRGZWF0dXJlLCBkczIpCgpgYGAKIyMg5bqU55So5YiwQUPkuIoKYGBge3J9CkFDX2RhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyX0FDLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKQUNfZGF0YSA8LSBBQ19kYXRhW3NlbGVjdGVkX2ZlYXR1cmVzLF0KQUNfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGRzMl9BQykpKQpjb2xuYW1lcyhBQ19kYXRhKSA8LSBOVUxMCkFDX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKEFDX2RhdGEsImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBBQ19sYWJlbCkKQUNfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gQUNfdGVzdF9kYXRhJGRhdGEsbGFiZWwgPSBBQ190ZXN0X2RhdGEkbGFiZWwpCgoj6K6h566X5re35reG55+p6Zi1CnByZWRpY3RfQUNfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IEFDX3Rlc3QpKQpBQ19jb25mdXNlX21hdHJpeF90ZXN0IDwtIHRhYmxlKEFDX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9BQ190ZXN0LCBkbm49YygidHJ1ZSIsInByZSIpKQpBQ19jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgPC0gcHJvcC50YWJsZShBQ19jb25mdXNlX21hdHJpeF90ZXN0LDEpCkFDX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCAgI+WIhuaekOWPkeiCsui9qOi/uQoKY29uZnVzZV9idWJibGVtYXQoQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wLGMoIkZpYnJvYmxhc3QiLCAiU01DMSIsICJGaWJyb215b2N5dGUiLCAiU01DMiIpLCBjKCJGaWJyb215b2N5dGUiLCAiU01DMSIsICJTTUMyIiksICJQQXRvQUMiKQoKCiNST0Pmm7Lnur8KeGdib29zdF9yb2MgPC0gcFJPQzo6bXVsdGljbGFzcy5yb2MoQUNfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X0FDX3Rlc3QpICPlpJrliIbnsbtST0MKeGdib29zdF9yb2NbWyJhdWMiXV0KCiMg6K6h566XQVJJIAphZGp1c3RlZFJhbmRJbmRleChwcmVkaWN0X0FDX3Rlc3QsIEFDX3Rlc3RfZGF0YSRsYWJlbCkKYGBgCiMgc2Fua2V5IHBsb3QKUEEgLT4gQUMKYGBge3IgZmlnLndpZHRoPTYsZmlnLmhlaWdodD00fQpzYW5rZXlfcGxvdChBQ19jb25mdXNlX21hdHJpeF90ZXN0LCBsYWJlbDEgPSBjKCJGaWJyb2JsYXN0IiwgIlNNQzEiLCAiU01DMiIpLCBsYWJlbDIgPSBjKCJGaWJyb2JsYXN0IiwgIlNNQzEiLCAiRmlicm9teW9jeXRlIiwgIlNNQzIiKSwgc2Vzc2lvbiA9ICJQQSAtPiBBQyIpCgp1bWFwcGxvdChkczJfQUMpCnVtYXBwbG90KGRzMl9QQSkKIyB1bWFwcGxvdChkczIsc3BsaXQuYnkgPSAiY29uZGl0aW9ucyIpCmBgYAoKCiPmiornu5PmnpzmipXlsITlm551bWFwCmBgYHtyfQpJZGVudHMoZHMyX0FDKSA8LSBwcmVkaWN0X0FDX3Rlc3QKZHMyX0FDJHByZWRpY3RfQUNfdGVzdCA8LSBwcmVkaWN0X0FDX3Rlc3QKdW1hcHBsb3QoZHMyX0FDLGdyb3VwLmJ5ID0gInByZWRpY3RfQUNfdGVzdCIpCklkZW50cyhkczJfQUMpIDwtIGRzMl9BQyRzZXVyYXRfY2x1c3RlcnMKYGBgCgojIOWPjeedgOWBmgojIOmAieaLqeeJueW+gWNvbW1vbiBnZW5lcyBvZiB0b3AgNTAwCiMjIOS9v+eUqOaJgOacieadpeiHqkFD55qE57uG6IOe6K6t57uD5YiG57G75ZmoCgpgYGB7cixmaWcuaGVpZ2h0PTYsZmlnLndpZHRoPTZ9CkFDX2RhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyX0FDLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKQUNfZGF0YSA8LSBBQ19kYXRhW3NlbGVjdGVkX2ZlYXR1cmVzLF0KQUNfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGRzMl9BQykpKQpjb2xuYW1lcyhBQ19kYXRhKSA8LSBOVUxMCgpBQ190cmFpbl9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoQUNfZGF0YSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IEFDX2xhYmVsKQoKQUNfdHJhaW4gPC0geGdiLkRNYXRyaXgoZGF0YSA9IEFDX3RyYWluX2RhdGEkZGF0YSxsYWJlbCA9IEFDX3RyYWluX2RhdGEkbGFiZWwpCgp4Z2JfQUNyYW0gPC0gbGlzdChldGEgPSAwLjIsIG1heF9kZXB0aCA9IDYsCiAgICAgICAgICAgICAgICAgIHN1YnNhbXBsZSA9IDAuNiwgIG51bV9jbGFzcyA9IGxlbmd0aCh0YWJsZShJZGVudHMoZHMyX0FDKSkpLAogICAgICAgICAgICAgICAgICBvYmplY3RpdmUgPSAibXVsdGk6c29mdG1heCIsIGV2YWxfbWV0cmljID0gJ21sb2dsb3NzJykKCmJzdF9tb2RlbDIgPC0geGdiLnRyYWluKHhnYl9BQ3JhbSwgQUNfdHJhaW4sIG5yb3VuZHMgPSAxMDAsIHZlcmJvc2UgPSAwKQoKIyDnibnlvoHmj5Dlj5YKaW1wb3J0YW5jZTIgPC0geGdiLmltcG9ydGFuY2UoY29sbmFtZXMoQUNfdHJhaW4pLCBtb2RlbCA9IGJzdF9tb2RlbDIpCmhlYWQoaW1wb3J0YW5jZTIpCnhnYi5nZ3Bsb3QuaW1wb3J0YW5jZShoZWFkKGltcG9ydGFuY2UyLDIwKSxuX2NsdXN0ZXJzID0gMSkgKyB0aGVtZV9idygpK3RoZW1lKAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siKSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG91ciA9ICJibGFjayIpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKQoKd3JpdGUuY3N2KGltcG9ydGFuY2UyLCAiLi9kYXRhdGFibGUvQUN0cmFpbl9mZWF0dXJlcy5jc3YiLCByb3cubmFtZXMgPSBGKQptdWx0aV9mZWF0dXJlcGxvdChoZWFkKGltcG9ydGFuY2UyLDkpJEZlYXR1cmUsIGRzMl9BQykKCmBgYAoKCiMjIOW6lOeUqOWcqFBB5LiK77yM6K6h566XQVJJCmBgYHtyfQpQQV9kYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMl9QQSwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpClBBX2RhdGEgPC0gUEFfZGF0YVtzZWxlY3RlZF9mZWF0dXJlcyxdClBBX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczJfUEEpKSkKY29sbmFtZXMoUEFfZGF0YSkgPC0gTlVMTAoKUEFfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoUEFfZGF0YSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IFBBX2xhYmVsKQoKUEFfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gUEFfdGVzdF9kYXRhJGRhdGEsbGFiZWwgPSBQQV90ZXN0X2RhdGEkbGFiZWwpCgoj6K6h566X5re35reG55+p6Zi1CnByZWRpY3RfUEFfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbDIsIG5ld2RhdGEgPSBQQV90ZXN0KSkKIApQQV9jb25mdXNlX21hdHJpeF90ZXN0IDwtIHRhYmxlKFBBX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9QQV90ZXN0LCBkbm49YygidHJ1ZSIsInByZSIpKQpQQV9jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgPC0gcHJvcC50YWJsZShQQV9jb25mdXNlX21hdHJpeF90ZXN0LDEpClBBX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCAgI+WIhuaekOWPkeiCsui9qOi/uQoKY29uZnVzZV9idWJibGVtYXQoUEFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wLGMoIkZpYnJvbXlvY3l0ZSIsICJTTUMxIiwgIlNNQzIiKSxjKCJGaWJyb2JsYXN0IiwgIlNNQzEiLCAiRmlicm9teW9jeXRlIiwgIlNNQzIiKSxzZXNzaW9uID0gIkFDdG9QQSIpCgojIOiuoeeul0FSSQphZGp1c3RlZFJhbmRJbmRleChwcmVkaWN0X1BBX3Rlc3QsIFBBX3Rlc3RfZGF0YSRsYWJlbCkKYGBgCiMjIOaKiue7k+aenOaKleWwhOWbnnVtYXAKYGBge3J9CklkZW50cyhkczJfUEEpIDwtIHByZWRpY3RfUEFfdGVzdApkczJfUEEkcHJlZGljdF9QQV90ZXN0IDwtIHByZWRpY3RfUEFfdGVzdAp1bWFwcGxvdChkczJfUEEsZ3JvdXAuYnkgPSAicHJlZGljdF9QQV90ZXN0IikKSWRlbnRzKGRzMl9QQSkgPC0gZHMyX1BBJHNldXJhdF9jbHVzdGVycwpgYGAKIyMgc2Fua2V5IHBsb3QKYGBge3J9CmxhYmVscyA8LSBjKCJGaWJyb2JsYXN0IiwgIlNNQzEiLCAiRmlicm9teW9jeXRlIiwgIlNNQzIiKQpsYWJlbHMyIDwtIGMoIkZpYnJvbXlvY3l0ZSIsICJTTUMxIiwgIlNNQzIiKQpzYW5rZXlfcGxvdChQQV9jb25mdXNlX21hdHJpeF90ZXN0LGxhYmVscyxsYWJlbHMyLHNlc3Npb24gPSAiQUMgLT4gUEEiKQpgYGAKCgojIHZhcmlmeSDpg6jliIYK55eF5Y+Y56iL5bqm6YeP5YyWCiMg5pWw5o2u6ZuGQ0FfZGF0YXNldDEKIyMgZHMy5YWo5L2T6K6t57uDCgpgYGB7cn0KSWRlbnRzKGRzMikgPC0gZHMyJHNldXJhdF9jbHVzdGVycwpkczJfZGF0YSA8LSBnZXRfZGF0YV90YWJsZShkczIsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpkczJfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGRzMikpKQoKaW5kZXggPC0gYygxOmRpbShkczJfZGF0YSlbMl0pICU+JSBzYW1wbGUoY2VpbGluZygwLjMqZGltKGRzMl9kYXRhKVsyXSksIHJlcGxhY2UgPSBGLCBwcm9iID0gTlVMTCkKY29sbmFtZXMoZHMyX2RhdGEpIDwtIE5VTEwKCmRzMl90cmFpbl9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoZHMyX2RhdGFbLC1pbmRleF0sImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBkczJfbGFiZWxbLWluZGV4XSkKZHMyX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGRzMl9kYXRhWyxpbmRleF0sImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBkczJfbGFiZWxbaW5kZXhdKQoKZHMyX3RyYWluIDwtIHhnYi5ETWF0cml4KGRhdGEgPSBkczJfdHJhaW5fZGF0YSRkYXRhLGxhYmVsID0gZHMyX3RyYWluX2RhdGEkbGFiZWwpCmRzMl90ZXN0IDwtIHhnYi5ETWF0cml4KGRhdGEgPSBkczJfdGVzdF9kYXRhJGRhdGEsbGFiZWwgPSBkczJfdGVzdF9kYXRhJGxhYmVsKQoKd2F0Y2hsaXN0IDwtIGxpc3QodHJhaW4gPSBkczJfdHJhaW4sIGV2YWwgPSBkczJfdGVzdCkKeGdiX3BhcmFtIDwtIGxpc3QoZXRhID0gMC4yLCBtYXhfZGVwdGggPSA2LCAKICAgICAgICAgICAgICAgICAgc3Vic2FtcGxlID0gMC42LCAgbnVtX2NsYXNzID0gbGVuZ3RoKHRhYmxlKElkZW50cyhkczIpKSksCiAgICAgICAgICAgICAgICAgIG9iamVjdGl2ZSA9ICJtdWx0aTpzb2Z0bWF4IiwgZXZhbF9tZXRyaWMgPSAnbWxvZ2xvc3MnKQoKYnN0X21vZGVsIDwtIHhnYi50cmFpbih4Z2JfcGFyYW0sIGRzMl90cmFpbiwgbnJvdW5kcyA9IDEwMCwgd2F0Y2hsaXN0LCB2ZXJib3NlID0gMCkKCmV2YWxfbG9zcyA8LSBic3RfbW9kZWxbWyJldmFsdWF0aW9uX2xvZyJdXVtbImV2YWxfbWxvZ2xvc3MiXV0KcGxvdF9seShkYXRhLmZyYW1lKGV2YWxfbG9zcyksIHggPSBjKDE6MTAwKSwgeSA9IGV2YWxfbG9zcykgJT4lIAogIGFkZF90cmFjZSh0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMrbGluZXMiLCAKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJibGFjayIsIGxpbmUgPSBsaXN0KGNvbG9yID0gIiMxRTkwRkZDNyIsIHdpZHRoID0gMSkpLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICIjMUU5MEZGODAiLCB3aWR0aCA9IDIpKSAlPiUgCiAgbGF5b3V0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJlcG9jaCIpLHlheGlzID0gbGlzdCh0aXRsZSA9ICJldmFsX21sb2dsb3NzIikpCmBgYAoKYGBge3IsZmlnLmhlaWdodD02LGZpZy53aWR0aD02fQojIOeJueW+geaPkOWPlgppbXBvcnRhbmNlIDwtIHhnYi5pbXBvcnRhbmNlKGNvbG5hbWVzKGRzMl90cmFpbiksIG1vZGVsID0gYnN0X21vZGVsKQpoZWFkKGltcG9ydGFuY2UpCnhnYi5nZ3Bsb3QuaW1wb3J0YW5jZShoZWFkKGltcG9ydGFuY2UsMjApLCBuX2NsdXN0ZXJzID0gMSkgKyB0aGVtZV9taW5pbWFsKCkKCm11bHRpX2ZlYXR1cmVwbG90KGhlYWQoaW1wb3J0YW5jZSw5KSRGZWF0dXJlLCBkczIpIApkczJfZ2VuZXMgPC0gaGVhZChpbXBvcnRhbmNlLCA1MDApICMj6YCJ5oupdG9wNTAwCgp3cml0ZS5jc3YoZHMyX2dlbmVzLCAiLi9kYXRhdGFibGUvZHMyX2ZlYXR1cmVzLmNzdiIsIHJvdy5uYW1lcyA9IEYpCgoKcHJlZGljdF9kczJfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IGRzMl90ZXN0KSkgJT4lIAoj5re35reG55+p6Zi1CmRzMl9jb25mdXNlX21hdHJpeF90ZXN0IDwtIHRhYmxlKGRzMl90ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfZHMyX3Rlc3QsIGRubj1jKCJ0cnVlIiwicHJlIikpCmRzMl9jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgPC0gcHJvcC50YWJsZShkczJfY29uZnVzZV9tYXRyaXhfdGVzdCwgMSkKZHMyX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcAoKeCA8LSBjKDA6NCkKeSA8LSBjKDA6NCkKY29uZnVzZV9idWJibGVtYXQoZHMyX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCx4LHksImRzMl90cmFpbiIpCgoKI1JPQ+absue6vwp4Z2Jvb3N0X3JvYyA8LSBwUk9DOjptdWx0aWNsYXNzLnJvYyhkczJfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X2RzMl90ZXN0KSAj5aSa5YiG57G7Uk9DCnhnYm9vc3Rfcm9jW1siYXVjIl1dICPlj6rpnIDopoHov5nkuKrlgLwKYWRqdXN0ZWRSYW5kSW5kZXgoZHMyX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9kczJfdGVzdCkgI+WIhuexu+WZqOaAp+iDvQpgYGAKCgpgYGB7cn0KdGVtcCA8LSBnZXRfZGF0YV90YWJsZShkczEsIGhpZ2h2YXIgPSBULCB0eXBlID0gImRhdGEiKQpkczFfZGF0YSA8LSBtYXRyaXgoZGF0YT0wLCBucm93ID0gZGltKGRzMl9kYXRhKVsxXSwgbmNvbCA9IGxlbmd0aChjb2xuYW1lcyh0ZW1wKSksIAogICAgICAgICAgICAgICAgICAgYnlyb3cgPSBGQUxTRSwgZGltbmFtZXMgPSBsaXN0KHJvd25hbWVzKGRzMl9kYXRhKSxjb2xuYW1lcyh0ZW1wKSkpCmZvcihpIGluIGludGVyc2VjdChyb3duYW1lcyhkczJfZGF0YSksIHJvd25hbWVzKHRlbXApKSl7CiAgZHMxX2RhdGFbaSxdIDwtIHRlbXBbaSxdCn0Kcm0odGVtcCkKZHMxX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczEpKSkKY29sbmFtZXMoZHMxX2RhdGEpIDwtIE5VTEwKZHMxX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGRzMV9kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMxX2xhYmVsKQpkczFfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMxX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gZHMxX3Rlc3RfZGF0YSRsYWJlbCkKCiPpooTmtYvnu5PmnpwKcHJlZGljdF9kczFfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IGRzMV90ZXN0KSkKCiPorqHnrpfmt7fmt4bnn6npmLUKZHMxX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdCA8LSB0YWJsZShkczFfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X2RzMV90ZXN0LCBkbm49YygidHJ1ZSIsInByZSIpKQpkczFfZGF0YV9jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgPC0gcHJvcC50YWJsZShkczFfZGF0YV9jb25mdXNlX21hdHJpeF90ZXN0LDEpCgoj57uY5Yi25re35reG55+p6Zi1CnggPC0gYygiRmlicm9teW9jeXRlIiwgIlNNQzEiLCAiU01DMiIpCnkgPC0gYygiRmlicm9ibGFzdCIsICJTTUMxIiwgIkZpYnJvbXlvY3l0ZSIsICJTTUMyIikKY29uZnVzZV9idWJibGVtYXQoZHMxX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wLHgseSwiZHMydG9kczEiKQoKZHMxX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdApkczFfZGF0YV9jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgICPliIbmnpDlj5HogrLovajov7kKI1JPQ+absue6vwp4Z2Jvb3N0X3JvYyA8LSBwUk9DOjptdWx0aWNsYXNzLnJvYyhkczFfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X2RzMV90ZXN0KSAj5aSa5YiG57G7Uk9DCnhnYm9vc3Rfcm9jW1siYXVjIl1dCgojIOiuoeeul0FSSSAKYWRqdXN0ZWRSYW5kSW5kZXgocHJlZGljdF9kczFfdGVzdCwgZHMxX3Rlc3RfZGF0YSRsYWJlbCkKYGBgCgojIyDmipXlsITlm551bWFwCmBgYHtyfQpJZGVudHMoZHMxKSA8LSBwcmVkaWN0X2RzMV90ZXN0CmRzMSRwcmVkaWN0X2RzMV90ZXN0IDwtIHByZWRpY3RfZHMxX3Rlc3QKdW1hcHBsb3QoZHMxLGdyb3VwLmJ5ID0gInByZWRpY3RfZHMxX3Rlc3QiKQpJZGVudHMoZHMxKSA8LSBkczEkc2V1cmF0X2NsdXN0ZXJzCmBgYAoKCgoKCiMg5Yag54q25Yqo6ISJ5pWw5o2u6ZuGCmBgYHtyfQpkczAgPC0gZHMwICU+JSBGaW5kTmVpZ2hib3JzKGRpbXMgPSAxOjIwKSAlPiUgRmluZENsdXN0ZXJzKHJlc29sdXRpb24gPSAwLjEpCnVtYXBwbG90KGRzMCkKZigiVEFHTE4iLGRzMCkKIyBkczBfbWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhkczAsbG9nZmMudGhyZXNob2xkID0gMC43LCBtaW4uZGlmZi5wY3QgPSAwLjIpCmBgYAoKYGBge3J9CnNlbGVjdGVkX2ZlYXR1cmVzIDwtIEFDX2dlbmVzJEZlYXR1cmUKdGVtcCA8LSBnZXRfZGF0YV90YWJsZShkczAsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpkczBfZGF0YSA8LSBtYXRyaXgoZGF0YT0wLCBucm93ID0gbGVuZ3RoKHNlbGVjdGVkX2ZlYXR1cmVzKSwgCiAgICAgICAgICAgICAgICAgICBuY29sID0gbGVuZ3RoKGNvbG5hbWVzKHRlbXApKSwgYnlyb3cgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3Qoc2VsZWN0ZWRfZmVhdHVyZXMsY29sbmFtZXModGVtcCkpKQpmb3IoaSBpbiBpbnRlcnNlY3Qoc2VsZWN0ZWRfZmVhdHVyZXMscm93bmFtZXModGVtcCkpKXsKICBkczBfZGF0YVtpLF0gPC0gdGVtcFtpLF0KfQpybSh0ZW1wKQoKZHMwX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczApKSkKY29sbmFtZXMoZHMwX2RhdGEpIDwtIE5VTEwKZHMwX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGRzMF9kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMwX2xhYmVsKQpkczBfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMwX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gZHMwX3Rlc3RfZGF0YSRsYWJlbCkKCiPorqHnrpfmt7fmt4bnn6npmLUKcHJlZGljdF9kczBfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IGRzMF90ZXN0KSkKCmRzMF9kYXRhX2NvbmZ1c2VfbWF0cml4X3Rlc3QgPC0gdGFibGUoZHMwX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9kczBfdGVzdCwgZG5uPWMoInRydWUiLCJwcmUiKSkKZHMwX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wIDwtIHByb3AudGFibGUoZHMwX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdCwxKQpgYGAKCgpgYGB7cn0KeCA8LSBjKCJkczBfMCIsICJkczBfMSIsICJkczBfMiIsICJkczBfMyIsICJkczBfNCIpCnkgPC0gYygiQUNfMCIsICJBQ18xIiwgIkFDXzIiKQoKcHJvcCA8LSBhcy5udW1lcmljKGRzMF9kYXRhX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCkKZGF0YSA8LSBleHBhbmQuZ3JpZCh4ID0geCwgeSA9IHkpICU+JSBiaW5kX2NvbHMocHJvcCA9IHByb3ApCnBsb3QgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0geCwgeSA9IHksIGNvbG91ciA9IHByb3AsIHNpemUgPSBwcm9wKSkgKwogIGdlb21fcG9pbnQoKSsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDEwKSkgKyAKICBsYWJzKHggPSAiY2x1c3RlcnMiLCB5ID0gImluZmVycmVkIGZyb20iKSArIHRoZW1lX2J3KCkKZ2dzYXZlKCIuL3Bsb3RzL0FDbW9kZWxfaHVtYW5jb3IucG5nIiwgcGxvdCA9IHBsb3QsIGRldmljZSA9IHBuZywgd2lkdGggPSA1LGhlaWdodCA9IDQpCgpkczBfZGF0YV9jb25mdXNlX21hdHJpeF90ZXN0CmRzMF9kYXRhX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCAgI+WIhuaekOWPkeiCsui9qOi/uQoKI1JPQ+absue6vwp4Z2Jvb3N0X3JvYyA8LSBwUk9DOjptdWx0aWNsYXNzLnJvYyhkczBfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X2RzMF90ZXN0KSAj5aSa5YiG57G7Uk9DCgojIOiuoeeul0FSSSAKYWRqdXN0ZWRSYW5kSW5kZXgocHJlZGljdF9kczBfdGVzdCwgZHMwX3Rlc3RfZGF0YSRsYWJlbCkKYGBgCgoKYGBge3J9CmxhYmVscyA8LSBsYXBwbHkobGV2ZWxzKElkZW50cyhkczJfQUMpKSwgcGFzdGUwLCAiX0FDIikgJT4lIGFzLmNoYXJhY3RlcigpCmxhYmVsczIgPC0gbGFwcGx5KGxldmVscyhJZGVudHMoZHMwKSksIHBhc3RlMCwgIl9kczAiKSAlPiUgYXMuY2hhcmFjdGVyKCkKc291cmNlcyA8LSByZXAoMDoobGVuZ3RoKGxhYmVscyktMSksIGVhY2ggPSBsZW5ndGgobGFiZWxzMikpICAj5rOo5oSP6L+Z6YeM55qEZWFjaOWSjHRpbWVz55qE5Yy65YirCmNvbG9ycyA8LSByZXAoY29sb3JzX2xpc3RbMTpsZW5ndGgobGFiZWxzKV0sIGVhY2ggPSBsZW5ndGgobGFiZWxzMikpCnRhcmdldHMgPC0gcmVwKGxlbmd0aChsYWJlbHMpKzA6KGxlbmd0aChsYWJlbHMyKS0xKSwgdGltZXMgPSBsZW5ndGgobGFiZWxzKSkKCnBsb3RfbHkodHlwZSA9ICJzYW5rZXkiLCBvcmllbnRhdGlvbiA9ICJoIiwKICAgIG5vZGUgPSBsaXN0KAogICAgICBsYWJlbCA9IGMobGFiZWxzLGxhYmVsczIpLCAKICAgICAgY29sb3IgPSBjb2xvcnNfbGlzdCwgcGFkID0gMTUsIHRoaWNrbmVzcyA9IDMwLAogICAgICBsaW5lID0gbGlzdCgKICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgd2lkdGggPSAxKSksCiAgICBsaW5rID0gbGlzdCgKICAgICAgc291cmNlID0gc291cmNlcywKICAgICAgdGFyZ2V0ID0gdGFyZ2V0cywKICAgICAgdmFsdWUgPSAgYXMubnVtZXJpYyhkczBfZGF0YV9jb25mdXNlX21hdHJpeF90ZXN0KSwKICAgICAgY29sb3IgPSBjb2xvcnMKICAgICAgKSkKYGBgCgoKCgpgYGB7cn0KIyBsb2FkKCIuL2luaXQuUkRhdGEiKQptdWx0aV9mZWF0dXJlcGxvdChoZWFkKGltcG9ydGFuY2UyLDkpJEZlYXR1cmUsIGRzMl9BQykKbXVsdGlfZmVhdHVyZXBsb3QoaGVhZChpbXBvcnRhbmNlMiw5KSRGZWF0dXJlLCBkczApCm11bHRpX2ZlYXR1cmVwbG90KGhlYWQoaW1wb3J0YW5jZTIsOSkkRmVhdHVyZSwgZHMxKQpmKCJNWUgxMSIsIGRzMl9BQykKdW1hcHBsb3QoZHMwKQpgYGAKCgojIOa3i+W3tOe7huiDngoKYGBge3J9Cmx5bV9kczIgPC0gc3Vic2V0KENBX2RhdGFzZXQyLCBpZGVudHMgPSBjKCcwJywnNCcsJzknKSkKbHltX2RzMiA8LSByZWFkUkRTKCJseW1fZHMyLnJkcyIpCklkZW50cyhseW1fZHMyKSA8LSBseW1fZHMyJGNvbmRpdGlvbnMKbHltX2RzMl9BQyA8LSBzdWJzZXQobHltX2RzMiwgaWRlbnRzID0gIkFDIikKbHltX2RzMl9QQSA8LSBzdWJzZXQobHltX2RzMiwgaWRlbnRzID0gIlBBIikKbHltX2RzMl9BQyA8LSBseW1fZHMyX0FDICU+JSBGaW5kTmVpZ2hib3JzKGRpbXMgPSAxOjIwKSAlPiUgRmluZENsdXN0ZXJzKHJlc29sdXRpb24gPSAwLjIpCnVtYXBwbG90KGx5bV9kczJfQUMpCmx5bV9kczJfUEEgPC0gbHltX2RzMl9QQSAlPiUgRmluZE5laWdoYm9ycyhkaW1zID0gMToyMCkgJT4lIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gMC4yKQp1bWFwcGxvdChseW1fZHMyX1BBKQoKZ2dzYXZlKCIuL3N1cHAvbHltX2RzMl9QQS5zdmciLCBwbG90ID0gdW1hcHBsb3QobHltX2RzMl9QQSksIGRldmljZSA9IHN2Zywgd2lkdGggPSA3LCBoZWlnaHQgPTYpCmdnc2F2ZSgiLi9zdXBwL2x5bV9kczJfQUMuc3ZnIiwgcGxvdCA9IHVtYXBwbG90KGx5bV9kczJfQUMpLCBkZXZpY2UgPSBzdmcsIHdpZHRoID0gNywgaGVpZ2h0ID02KQpgYGAKCiMjIOeUqFBB55qEbHlt6K6t57uDCmBgYHtyfQpseW1fUEFfZGF0YSA8LSBnZXRfZGF0YV90YWJsZShseW1fZHMyX1BBLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKbHltX1BBX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhseW1fZHMyX1BBKSkpCgpzZXQuc2VlZCg3KQppbmRleCA8LSBjKDE6ZGltKGx5bV9QQV9kYXRhKVsyXSkgJT4lIHNhbXBsZShjZWlsaW5nKDAuMypkaW0obHltX1BBX2RhdGEpWzJdKSwgcmVwbGFjZSA9IEYsIHByb2IgPSBOVUxMKQpjb2xuYW1lcyhseW1fUEFfZGF0YSkgPC0gTlVMTApseW1fUEFfdHJhaW5fZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGx5bV9QQV9kYXRhWywtaW5kZXhdLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gbHltX1BBX2xhYmVsWy1pbmRleF0pCmx5bV9QQV90ZXN0X2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhseW1fUEFfZGF0YVssaW5kZXhdLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gbHltX1BBX2xhYmVsW2luZGV4XSkKCmx5bV9QQV90cmFpbiA8LSB4Z2IuRE1hdHJpeChkYXRhID0gbHltX1BBX3RyYWluX2RhdGEkZGF0YSxsYWJlbCA9IGx5bV9QQV90cmFpbl9kYXRhJGxhYmVsKQpseW1fUEFfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gbHltX1BBX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gbHltX1BBX3Rlc3RfZGF0YSRsYWJlbCkKCndhdGNobGlzdCA8LSBsaXN0KHRyYWluID0gbHltX1BBX3RyYWluLCBldmFsID0gbHltX1BBX3Rlc3QpCnhnYl9wYXJhbSA8LSBsaXN0KGV0YSA9IDAuMiwgbWF4X2RlcHRoID0gNiwgCiAgICAgICAgICAgICAgICAgIHN1YnNhbXBsZSA9IDAuNiwgIG51bV9jbGFzcyA9IGxlbmd0aCh0YWJsZShJZGVudHMobHltX2RzMl9QQSkpKSwKICAgICAgICAgICAgICAgICAgb2JqZWN0aXZlID0gIm11bHRpOnNvZnRtYXgiLCBldmFsX21ldHJpYyA9ICdtbG9nbG9zcycpCgpic3RfbW9kZWwgPC0geGdiLnRyYWluKHhnYl9wYXJhbSwgbHltX1BBX3RyYWluLCBucm91bmRzID0gMTAwLCB3YXRjaGxpc3QsIHZlcmJvc2UgPSAwKQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTYsZmlnLndpZHRoPTZ9CiMg54m55b6B5o+Q5Y+WCmltcG9ydGFuY2UgPC0geGdiLmltcG9ydGFuY2UoY29sbmFtZXMobHltX1BBX3RyYWluKSwgbW9kZWwgPSBic3RfbW9kZWwpCmhlYWQoaW1wb3J0YW5jZSkKeGdiLmdncGxvdC5pbXBvcnRhbmNlKGhlYWQoaW1wb3J0YW5jZSwyMCksbl9jbHVzdGVycyA9IDEpICsgdGhlbWVfYncoKSt0aGVtZSgKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvdXIgPSAiYmxhY2siKSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkKCmx5bV9QQV9nZW5lcyA8LSBoZWFkKGltcG9ydGFuY2UsIDUwMCkgIyPpgInmi6l0b3A1MDAKbXVsdGlfZmVhdHVyZXBsb3QobHltX1BBX2dlbmVzJEZlYXR1cmVbMTo5XSxseW1fZHMyX1BBLGxhYmVscyA9ICIiKQp3cml0ZS5jc3YobHltX1BBX2dlbmVzLCIuL2RhdGF0YWJsZS9seW1fUEFfZmVhdHVyZXMuY3N2Iiwgcm93Lm5hbWVzID0gRikKI+a3t+a3huefqemYtQpwcmVkaWN0X2x5bV9QQV90ZXN0IDwtIHJvdW5kKHByZWRpY3QoYnN0X21vZGVsLCBuZXdkYXRhID0gbHltX1BBX3Rlc3QpKQoKbHltX1BBX2NvbmZ1c2VfbWF0cml4X3Rlc3QgPC0gdGFibGUobHltX1BBX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9seW1fUEFfdGVzdCwgZG5uPWMoInRydWUiLCJwcmUiKSkKbHltX1BBX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCA8LSBwcm9wLnRhYmxlKGx5bV9QQV9jb25mdXNlX21hdHJpeF90ZXN0LCAxKQpseW1fUEFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wCgp4IDwtIGMoIlBBX2x5bV8wIiwgIlBBX2x5bV8xIiwgIlBBX2x5bV8yIiwgIlBBX2x5bV8zIiwgIlBBX2x5bV80IiwgIlBBX2x5bV81IikKeSA8LSBjKCJQQV9seW1fMCIsICJQQV9seW1fMSIsICJQQV9seW1fMiIsICJQQV9seW1fMyIsICJQQV9seW1fNCIsICJQQV9seW1fNSIpCgpwcm9wIDwtIGFzLm51bWVyaWMobHltX1BBX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCkKZGF0YSA8LSBleHBhbmQuZ3JpZCh4ID0geCwgeSA9IHkpICU+JSBiaW5kX2NvbHMocHJvcCA9IHByb3ApCnBsb3QgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0geCwgeSA9IHksIGNvbG91ciA9IHByb3AsIHNpemUgPSBwcm9wKSkgKwogIGdlb21fcG9pbnQoKSsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDEwKSkgKyAKICBsYWJzKHggPSAiY2x1c3RlcnMiLCB5ID0gImluZmVycmVkIGZyb20iKSArIHRoZW1lX2J3KCkKZ2dzYXZlKCIuL3Bsb3RzL1BBbHltbW9kZWwucG5nIiwgcGxvdCA9IHBsb3QsIGRldmljZSA9IHBuZywgd2lkdGggPSA3LGhlaWdodCA9NikKYGBgCgoKIyMg55SoQUPnmoRseW3pqozor4EKYGBge3J9Cmx5bV9BQ19kYXRhIDwtIGdldF9kYXRhX3RhYmxlKGx5bV9kczJfQUMsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpseW1fQUNfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGx5bV9kczJfQUMpKSkKY29sbmFtZXMobHltX0FDX2RhdGEpIDwtIE5VTEwKbHltX0FDX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGx5bV9BQ19kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gbHltX0FDX2xhYmVsKQpseW1fQUNfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gbHltX0FDX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gbHltX0FDX3Rlc3RfZGF0YSRsYWJlbCkKCnByZWRpY3RfbHltX0FDX3Rlc3QgPC0gcm91bmQocHJlZGljdChic3RfbW9kZWwsIG5ld2RhdGEgPSBseW1fQUNfdGVzdCkpCgpseW1fQUNfY29uZnVzZV9tYXRyaXhfdGVzdCA8LSB0YWJsZShseW1fQUNfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X2x5bV9BQ190ZXN0LCBkbm49YygidHJ1ZSIsInByZSIpKQpseW1fQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wIDwtIHByb3AudGFibGUobHltX0FDX2NvbmZ1c2VfbWF0cml4X3Rlc3QsIDEpCmx5bV9BQ19jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AKCgp4IDwtIGMoIlBBX2x5bV8wIiwgIlBBX2x5bV8xIiwgIlBBX2x5bV8yIiwgIlBBX2x5bV8zIiwgIlBBX2x5bV80IiwgIlBBX2x5bV81IikKeSA8LSBjKCJQQV9seW1fMCIsICJQQV9seW1fMSIsICJQQV9seW1fMiIsICJQQV9seW1fMyIsICJQQV9seW1fNCIpCgpwcm9wIDwtIGFzLm51bWVyaWMobHltX0FDX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCkKZGF0YSA8LSBleHBhbmQuZ3JpZCh4ID0geCwgeSA9IHkpICU+JSBiaW5kX2NvbHMocHJvcCA9IHByb3ApCnBsb3QgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0geCwgeSA9IHksIGNvbG91ciA9IHByb3AsIHNpemUgPSBwcm9wKSkgKwogIGdlb21fcG9pbnQoKSsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDEwKSkgKyAKICBsYWJzKHggPSAiY2x1c3RlcnMiLCB5ID0gImluZmVycmVkIGZyb20iKSArIHRoZW1lX2J3KCkKZ2dzYXZlKCIuL3Bsb3RzL1BBbHltbW9kZWxfQUMucG5nIiwgcGxvdCA9IHBsb3QsIGRldmljZSA9IHBuZywgd2lkdGggPSA3LGhlaWdodCA9IDYpCgp4Z2Jvb3N0X3JvY1tbImF1YyJdXQphZGp1c3RlZFJhbmRJbmRleChwcmVkaWN0X2x5bV9BQ190ZXN0LCBseW1fQUNfdGVzdF9kYXRhJGxhYmVsKQpseW1fQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wCgpzYW5rZXlfcGxvdChseW1fQUNfY29uZnVzZV9tYXRyaXhfdGVzdCxzZXNzaW9uID0gIlBBdG9BQ19seW0iKQpgYGAKCgpgYGB7cn0KbGFiZWxzIDwtIGxhcHBseShsZXZlbHMoSWRlbnRzKGx5bV9kczJfUEEpKSwgcGFzdGUwLCAiX2x5bVBBIikgJT4lIGFzLmNoYXJhY3RlcigpCmxhYmVsczIgPC0gbGFwcGx5KGxldmVscyhJZGVudHMobHltX2RzMl9BQykpLCBwYXN0ZTAsICJfbHltQUMiKSAlPiUgYXMuY2hhcmFjdGVyKCkKc291cmNlcyA8LSByZXAoMDo1LCBlYWNoID0gNSkgICPms6jmhI/ov5nph4znmoRlYWNo5ZKMdGltZXPnmoTljLrliKsKY29sb3JzIDwtIHJlcChjb2xvcnNfbGlzdFsxOjZdLCBlYWNoID0gNSkKdGFyZ2V0cyA8LSByZXAoNjoxMCwgdGltZXMgPSA2KQoKcGxvdF9seSh0eXBlID0gInNhbmtleSIsIG9yaWVudGF0aW9uID0gImgiLAogICAgbm9kZSA9IGxpc3QoCiAgICAgIGxhYmVsID0gYyhsYWJlbHMsbGFiZWxzMiksIAogICAgICBjb2xvciA9IGNvbG9yc19saXN0LCBwYWQgPSAxNSwgdGhpY2tuZXNzID0gMzAsCiAgICAgIGxpbmUgPSBsaXN0KAogICAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgICB3aWR0aCA9IDEpKSwKICAgIGxpbmsgPSBsaXN0KAogICAgICBzb3VyY2UgPSBzb3VyY2VzLAogICAgICB0YXJnZXQgPSB0YXJnZXRzLAogICAgICB2YWx1ZSA9ICBhcy5udW1lcmljKGx5bV9BQ19jb25mdXNlX21hdHJpeF90ZXN0KSwKICAgICAgY29sb3IgPSBjb2xvcnMKICAgICAgKSkKCgp1bWFwcGxvdChseW1fZHMyX0FDKQp1bWFwcGxvdChseW1fZHMyX1BBKQpgYGAKCgojIyBmdW5jdGlvbnMgc2V0CmBgYHtyfQpzYW5rZXlfcGxvdCA8LSBmdW5jdGlvbihjb25mdXNlX21hdHJpeCwgbGFiZWwxLCBsYWJlbDIsIHNlc3Npb24gPSAic2Vzc2lvbiIpCnsKICBzb3VyY2VzIDwtIHJlcCgwOihsZW5ndGgobGFiZWwxKS0xKSwgZWFjaCA9IGxlbmd0aChsYWJlbDIpKSAgI+azqOaEj+i/memHjOeahGVhY2jlkox0aW1lc+eahOWMuuWIqwogIGNvbG9ycyA8LSByZXAoYWVyb19jb2xvcnNfbGlzdFsxOmxlbmd0aChsYWJlbDEpXSwgZWFjaCA9IGxlbmd0aChsYWJlbDIpKQogIHRhcmdldHMgPC0gcmVwKGxlbmd0aChsYWJlbDEpKzA6KGxlbmd0aChsYWJlbDIpLTEpLCB0aW1lcyA9IGxlbmd0aChsYWJlbDEpKQoKICBwbG90X2x5KHR5cGUgPSAic2Fua2V5Iiwgb3JpZW50YXRpb24gPSAiaCIsCiAgICAgIG5vZGUgPSBsaXN0KAogICAgICAgIGxhYmVsID0gYyhsYWJlbDEsbGFiZWwyKSwgCiAgICAgICAgY29sb3IgPSBjb2xvcnNfbGlzdCwgcGFkID0gMTUsIHRoaWNrbmVzcyA9IDMwLAogICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsYWNrIiwgd2lkdGggPSAxKSksCiAgICAgIGxpbmsgPSBsaXN0KAogICAgICAgIHNvdXJjZSA9IHNvdXJjZXMsIHRhcmdldCA9IHRhcmdldHMsCiAgICAgICAgdmFsdWUgPSAgYXMubnVtZXJpYyhjb25mdXNlX21hdHJpeCksCiAgICAgICAgY29sb3IgPSBjb2xvcnMKICAgICAgICApKSAlPiUgbGF5b3V0KHRpdGxlPXNlc3Npb24sIGZvbnQ9bGlzdChmYW1pbHkgPSAiQXJpYWwiLHNpemUgPSAyMCwgY29sb3IgPSAnYmxhY2snKSkKfQoKCmNvbmZ1c2VfYnViYmxlbWF0IDwtIGZ1bmN0aW9uKGNvbmZ1c2VfbWF0cml4X3Byb3AsIGxhYmVsMSwgbGFiZWwyLCBzZXNzaW9uID0gInNlc3Npb24iKQp7CnByb3AgPC0gYXMubnVtZXJpYyhjb25mdXNlX21hdHJpeF9wcm9wKQpkYXRhIDwtIGV4cGFuZC5ncmlkKHggPSBsYWJlbDEsIHkgPSBsYWJlbDIpICU+JSBiaW5kX2NvbHMocHJvcCA9IHByb3ApCnBsb3QgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0geCwgeSA9IHksIGNvbG91ciA9IHByb3AsIHNpemUgPSBwcm9wKSkgKwogIGdlb21fcG9pbnQoKSsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAsIDEwKSkgKyAKICBsYWJzKHggPSAiY2x1c3RlcnMiLCB5ID0gImluZmVycmVkIGZyb20iKSArIHRoZW1lX2J3KCkKCmdnc2F2ZShwYXN0ZTAoc2Vzc2lvbiwgIi5zdmciKSwgcGxvdCA9IHBsb3QsIGRldmljZSA9IHN2Zywgd2lkdGggPSA1LGhlaWdodCA9IDQpCn0KCiMjIOi/lOWbnuacgOWkp+eahOamgueOh+WvueW6lOeahGluZGV4CmZ1bmMgPC0gZnVuY3Rpb24ocywgaWRlbnQpCnsKICBpZihtYXgocyk+MS4yL2xlbmd0aChpZGVudCkpCiAgICByZXR1cm4oaWRlbnRbd2hpY2gocyA9PSBtYXgocykpXSkKICBlbHNlCiAgICByZXR1cm4oInVuYXNzaWduZWQiKQp9CgpgYGAKCmBgYHtyfQphZXJvX2NvbG9yc19saXN0IDwtIGFzLmNoYXJhY3RlcihsYXBwbHkoY29sb3JzX2xpc3QsIHBhc3RlMCwgIkEwIikpICPpgI/mmI7ljJbpopzoibIKYGBgCgoKCkFkZCBhIG5ldyBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKi4KCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ3RybCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLgoKVGhlIHByZXZpZXcgc2hvd3MgeW91IGEgcmVuZGVyZWQgSFRNTCBjb3B5IG9mIHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLiBDb25zZXF1ZW50bHksIHVubGlrZSAqS25pdCosICpQcmV2aWV3KiBkb2VzIG5vdCBydW4gYW55IFIgY29kZSBjaHVua3MuIEluc3RlYWQsIHRoZSBvdXRwdXQgb2YgdGhlIGNodW5rIHdoZW4gaXQgd2FzIGxhc3QgcnVuIGluIHRoZSBlZGl0b3IgaXMgZGlzcGxheWVkLgo=